package com.freecloud.bittorrent.demo.handler;

import cn.hutool.core.codec.Base64;
import com.freecloud.bitorrent.common.util.bencode.BencodingUtils;
import com.freecloud.bittorrent.demo.util.Node;
import com.freecloud.bittorrent.demo.util.UniqueBlockingQueue;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Map;

/**
 * @Author: maomao
 * @Date: 2021/11/22 4:56 下午
 */
@Slf4j
public class DHTHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    public static final UniqueBlockingQueue NODES_QUEUE = new UniqueBlockingQueue();

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, DatagramPacket datagramPacket)
        throws Exception {
        log.debug("接收到外部请求 : {}", datagramPacket);
        log.debug("channelHandlerContext : {}", channelHandlerContext);
        log.debug("datagramPacket : {}", datagramPacket);
        byte[] buff = new byte[datagramPacket.content().readableBytes()];
        datagramPacket.content().readBytes(buff);

        channelHandlerContext.executor().execute(() -> {
            try {
                Map<String, ?> msg = BencodingUtils.decode(buff);
                if (msg == null || msg.get("y") == null) {
                    return;
                }
                log.info("msg : {}", msg);
                byte[] y = (byte[])msg.get("y");
                if (y.length > 0 && y[0] == 'q') {
                    //请求 Queries
                    log.info("Queries : {}", y);
                    //onQuery(msg, datagramPacket.sender());
                } else if (y.length > 0 && y[0] == 'r') {
                    //回复 Responses
                    log.info("Responses : {}", msg);
                    onResponse(msg, datagramPacket.sender());
                }
            } catch (Exception e) {
                log.info("error buffer: {}", new String(buff));
                log.error("错误：{}", e);
            }
        });
    }

    private void onResponse(Map<String, ?> map, InetSocketAddress sender) throws UnknownHostException {
        //transaction id
        byte[] t = (byte[])map.get("t");
        //由于在我们发送查询 DHT 节点请求时，构造的查询 transaction id 为字符串 find_node（见 findNode 方法），所以根据字符串判断响应请求即可
        String type = new String(t);
        if ("find_node".equals(type)) {
            if (map.containsKey("r")) {
                resolveNodes((Map)map.get("r"));
            } else if (map.containsKey("e")) {
                //变种协议？
                resolveNodes((Map)map.get("e"));
            }

        } else if ("ping".equals(type)) {

        } else if ("get_peers".equals(type)) {

        } else if ("announce_peer".equals(type)) {

        }
    }

    /**
     * 解析响应内容中的 DHT 节点信息
     *
     * @param r
     */
    private void resolveNodes(Map r) throws UnknownHostException {

        if (r == null)
            return;

        byte[] nodes = (byte[])r.get("nodes");

        if (nodes == null)
            return;

        for (int i = 0; i < nodes.length / 26 * 26; i += 26) {
            //limit the node queue size
				/*if (NODES_QUEUE.size() > 15000)
					continue;*/
            InetAddress ip =
                InetAddress.getByAddress(new byte[] {nodes[i + 20], nodes[i + 21], nodes[i + 22], nodes[i + 23]});
            InetSocketAddress address =
                new InetSocketAddress(ip, (0x0000FF00 & (nodes[i + 24] << 8)) | (0x000000FF & nodes[i + 25]));
            byte[] nid = new byte[20];
            System.arraycopy(nodes, i, nid, 0, 20);
            NODES_QUEUE.offer(new Node(nid, address));
            log.debug("get nodeId[{}] address=[{}] ", Base64.encode(nid), address);
        }
    }
}
